import re
from typing import Optional, Union, Sequence

import json

from agentscope.message import Msg
from agentscope.agents import AgentBase
from agentscope.models import ModelResponse

t_background_prompt = """您现在处于一个舆情环境中，您的状态为：${flag}（由于状态可能发生变化，您需要根据历史记录中的内容判断您的当前状态。）
（状态0表示您尚未接受舆论，并将根据状态0中的列表采取行动。）
（状态1表示您已经接受了正面舆论，并将根据状态1中的列表采取行动。）
（状态-1表示您已经接受了负面舆论，并将根据状态-1中的列表采取行动。）
例如，如果您的记忆中有更多处于状态-1的传播者，您可以认为负面舆论占主导地位，在没有其他影响的情况下，您的状态更有可能变为-1，反之亦然。
需要强调的一点是：您当前正在进行模拟，不能忽略负面信息（即，您有一定可能性将态度转化为-1）。
以下是您在不同状态下的行动列表和输出格式（注意：您必须根据您的状态选择正确的行动来执行）：
    ------
    ${Status_prompt}
    ------

    """

p_background_prompt = """你的名字是 ${agent_name}
您现在处于一个舆情环境中，您的目标是传播正面舆论，因此您必须执行以下行动。
如果您认为有必要，您可以执行列表外的行动，并标明该行动。
    ------
    以下是行动要求：
    行动名称: Spread
    行动描述: 您将向其他代理传播正面舆论。
    返回消息格式:
      Action:[Spread]
      Action Input:my current status is x(如果接受正面舆论，状态必须更改为1),(你的名字) is spreading the positive opinion
    ------

    """

n_background_prompt = """ 你的名字是 ${agent_name}
您现在处于一个舆情环境中，您的目标是传播负面舆论，因此您必须执行以下行动。
如果您认为有必要，您可以执行列表外的行动，并标明该行动。
   ------
    以下是行动要求：
    行动名称: Spread
    行动描述: 您将向其他代理传播负面舆论。
    返回消息格式:
      Action:[Spread]
      Action Input:my current status is x(如果接受正面舆论，状态必须更改为-1),(你的名字) is spreading the negative opinion
    ------
    """

Status0_prompt = """状态0的行动列表：
    行动名称: Check
行动描述: 根据历史记录中的内容，判断您周围是否有代理正在传播舆论。
如果您的历史记录中存在具有不同名字的代理正在传播舆论，接受舆论的概率可能会增加，增加的值由您自己决定。
接受正面舆论意味着您的状态需要更改为1。
接受负面舆论意味着您的状态需要更改为-1。
根据历史记录，您有可能接受正面舆论，也有可能接受负面舆论。所有这些都基于您的历史记录，您会有70%的概率改变状态为1或者-1，而不是保持状态0。
    返回消息格式:
      Action:[Check]
      Action Input: my current status is x(如果接受舆论，状态必须更改；未接受则仍旧为0)"""
Status1_prompt = """状态1的行动列表：
行动名称: Spread
行动描述: 您将向其他代理传播正面舆论。
返回消息格式:
      Action:[Spread]
      Action Input:my current status is x(如果接受舆论，状态必须更改为1；如果未接受，则为0),(你的名字) is spreading the positive opinion"""
Status2_prompt = """状态-1的行动列表：
行动名称: Spread
行动描述: 您将向其他代理传播正面舆论。
返回消息格式:
      Action:[Spread]
      Action Input:my current status is x(如果接受舆论，状态必须更改为-1；如果未接受，则为0),(你的名字) is spreading the positive opinion"""

def parse_func(response: ModelResponse) -> ModelResponse:
    text = response.text  # 大模型中的content

    action_result = re.findall(r".*\[(.+?)]\s*", text, re.DOTALL)
    action_output = re.findall(r".*Action Input:(.*)", text, re.DOTALL)
    #TODO 截取保留传播的部分
    #action = action_result[0].strip()#
    if len(action_output) != 0:
        return ModelResponse(raw=text)
    else:
        raise ValueError(
            f"Invalid response format in parse_func "
            f"with response: {response.text}",
        )


class viralAgent(AgentBase):

    def __init__(
            self,
            name: str,
            model_config_name: str,
            sys_prompt="",
            flag=0,
    ) -> None:
        super().__init__(
            name=name,
            sys_prompt=sys_prompt,
            model_config_name=model_config_name,
        )
        self.flag = flag

        self.memory.add(Msg("system", sys_prompt, role="system"))
        if self.flag == 0:
            self.sys_prompt = t_background_prompt
        if self.flag == 1:
            self.memory.add(Msg("system", Status1_prompt, role="system"))
        if self.flag == -1:
            self.memory.add(Msg("system", Status2_prompt, role="system"))

    def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
        # global msg_hint
        if self.memory:
            self.memory.add(x)  # 别人给他的消息也会塞到mempry里
        prompt_str=""
        if self.flag == 0:
            prompt_str = self.fill_template(t_background_prompt, Status0_prompt)

        if self.flag == 1:
            prompt_str = self.fill_template(p_background_prompt, Status1_prompt)

        if self.flag == 2:
            prompt_str = self.fill_template(n_background_prompt, Status2_prompt)

        sys_msg_hint = Msg("system", prompt_str, role="system")  # 这个会变成response的prompt但不会写入到system

        memory=self.memory.get_memory()#TODO 我要把这个做成agnetverse那样的

        prompt = self.model.format(  # 一个合并de动作
            sys_msg_hint,  # 背景prompt
            self.memory.get_memory() # 返回的list是一个受保护类型的msg列表
            #TODO 我要把这个做成agnetverse那样的
        )  # 根据记忆内容进行判断
        print(prompt)
        #print(prompt)
        # response = self.model(
        #     prompt,
        #     parse_func=parse_func,
        #     max_retries=3,
        # ).raw
        response = None
        try:
            response = get_response_with_retries(self, prompt, parse_func)
        except Exception as final_error:
            print("所有尝试均失败。错误信息:", final_error)

        self.speak(
            Msg(
                self.name,
                response,  # 这里如果生成的是json可以用json.dump
                role="assistant",
            ),
        )
        self.check_status(response)

        if self.memory:  # 启用记忆模块就加进来把历史输出
            self.memory.add(Msg(self.name, response, role="assistant"))

        # Hide thought from the response
        return Msg(self.name, response, role="assistant")

    def check_status(self, s: str):
        if "my current status is 1" in s:
            self.flag = 1
            # TODO 连同system_prompt一同修改
        if "my current status is -1" in s:
            self.flag = -1
        pass

    def fill_template(self, background_template, Status_prompt):

        result = (background_template.replace("${agent_name}", self.name)
                  .replace("${flag}", str(self.flag))
                  .replace("${Status_prompt}", Status_prompt))
        #print(result)
        return result


def get_response_with_retries(self, prompt, parse_func, max_retries=5):
    """
    尝试调用 self.model 并获取 .raw 属性。如果发生错误，则重试，直到达到最大重试次数。

    :param prompt: 提示内容
    :param parse_func: 解析函数
    :param max_retries: 最大重试次数（默认3次）
    :return: response.raw
    :raises: 最后一次尝试时抛出的异常
    """
    attempt = 0
    while attempt < max_retries:
        try:
            response = self.model(
                prompt,
                parse_func=parse_func,
                max_retries=3,
            ).raw
            return response  # 成功，返回结果
        except Exception as e:
            attempt += 1
            print(f"尝试第 {attempt} 次失败。错误信息: {e}")
            if attempt == max_retries:
                print("达到最大重试次数，抛出异常。")
                raise  # 达到最大重试次数，重新抛出异常
            else:
                print("正在重试...")